﻿using System;
using System.IO;
using Viyi.Util.Codec;
using Xunit;
using Xunit.Abstractions;

namespace Viyi.Util.Test.Codec
{
    public class TestBase64Codec
    {
        static readonly Random random = new Random();
        readonly Base64Codec base64 = new Base64Codec();
        readonly ITestOutputHelper output;

        byte[] CreateRandomData(int length)
        {
            byte[] data = new byte[length];
            random.NextBytes(data);
            return data;
        }

        public TestBase64Codec(ITestOutputHelper output)
        {
            this.output = output;
        }

        [Fact]
        public void TestSimpleCodec()
        {
            TestSimpleCodec(87);
            TestSimpleCodec(88);
            TestSimpleCodec(89);
        }

        void TestSimpleCodec(int length)
        {
            byte[] data = CreateRandomData(length);
            var expect = Convert.ToBase64String(data);
            var code = base64.Encode(data);
            Assert.Equal(expect, code);

            byte[] decodeData = base64.Decode(expect);
            Assert.Equal(data, decodeData);
        }

        [Fact]
        public void TestLineBreaks()
        {
            const int dataLength = 130;
            const int lineLength = 32;

            byte[] data = CreateRandomData(dataLength);
            var base64WithLineBreak = new Base64Codec { LineLength = lineLength };

            var expect = Convert.ToBase64String(data);
            var code = base64WithLineBreak.Encode(data);
            var lines = code.Split(new[] { base64WithLineBreak.EndOfLine }, StringSplitOptions.None);
            Assert.Equal(expect, string.Join(string.Empty, lines));

            var chars = (int) (Math.Ceiling(dataLength / 3.0) * 4);
            var lastLineLength = chars % lineLength;
            var linesCount = chars / lineLength + (lastLineLength > 0 ? 1 : 0);
            Assert.Equal(linesCount, lines.Length);

            for (int i = linesCount - 2; i >= 0; i--)
            {
                Assert.Equal(lineLength, lines[i].Length);
            }
            if (lastLineLength == 0)
            {
                lastLineLength = lineLength;
            }
            Assert.Equal(lastLineLength, lines[linesCount - 1].Length);
        }

        [Fact]
        public void TestSimepleEncodeToTextWriter()
        {
            byte[] data1 = new byte[55];
            byte[] data2 = new byte[66];
            random.NextBytes(data1);
            random.NextBytes(data2);

            byte[] dataAll = new byte[data1.Length + data2.Length];
            Array.Copy(data1, dataAll, data1.Length);
            Array.Copy(data2, 0, dataAll, data1.Length, data2.Length);

            var expect = Convert.ToBase64String(dataAll);

            using (var writer = new StringWriter())
            {
                base64.Encode(writer, new[] { data1, data2 });
                Assert.Equal(expect, writer.ToString());
            }
        }

        [Fact]
        public void TestSimpleDecodeToStream()
        {
            const int baseMin = 150;
            const int baseMax = 300;

            TestSimpleDecodeToStream(random.Next(baseMin, baseMax) * 3);
            TestSimpleDecodeToStream(random.Next(baseMin, baseMax) * 3 + 1);
            TestSimpleDecodeToStream(random.Next(baseMin, baseMax) * 3 + 2);
        }

        void TestSimpleDecodeToStream(int length)
        {
            byte[] expect = new byte[length];
            random.NextBytes(expect);

            // 单行
            var text = Convert.ToBase64String(expect, Base64FormattingOptions.InsertLineBreaks);
            using (var stream = new MemoryStream())
            {
                base64.Decode(stream, new[] { text });
                byte[] result = stream.ToArray();
                Assert.Equal(expect, result);
            }

            // 多行
            var lines = Convert
                .ToBase64String(expect, Base64FormattingOptions.InsertLineBreaks)
                .Split('\n');
            using (var stream = new MemoryStream())
            {
                base64.Decode(stream, new[] { text });
                byte[] result = stream.ToArray();
                Assert.Equal(expect, result);
            }
        }
    }
}
